package com.code.wordhelper.utils;


import com.code.wordhelper.model.ImageByteData;
import com.code.wordhelper.model.ImageData;
import com.code.wordhelper.testbean.Inner;
import com.code.wordhelper.testbean.InnerTwo;
import org.apache.poi.ooxml.POIXMLDocument;
import org.apache.poi.openxml4j.opc.OPCPackage;
import org.apache.poi.sl.usermodel.PictureData;
import org.apache.poi.util.Units;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import sun.misc.BASE64Decoder;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 处理文本替换
 */
public class ParagraphTemplateUtils {
    private final static Pattern TEXT_PLACE_HOLDER = Pattern.compile("\\$\\{([\\w\\.]+)\\}");
    private final static String PLACE_HOLDER_PREFIX = "$";
    private final static String PLACE_HOLDER_SUFFIX = "}";
    private final static String MULTI_FIELD_SEPERATOR = ".";
    private final static String MULTI_FIELD_SEPERATOR_SPLIT = "\\.";
    private final static String INDEX_SEPERATOR_SPLIT = "\\w+\\[(\\d+)\\]";
    private final static Pattern INDEX_SEPERATOR_SPLIT_PATTERN = Pattern.compile("(\\w+)\\[(\\d+)\\]");

    private static Logger logger = LoggerFactory.getLogger(WordTemplateUtils.class);

    public static void replaceParagraphs(XWPFDocument document, Map<String, Object> dataMap) throws Exception {
        List<XWPFParagraph> paragraphList = document.getParagraphs();

        for (int i = 0; i < paragraphList.size(); i++) {
            XWPFParagraph paragraph = paragraphList.get(i);
            if (!PatternUtils.match(paragraph.getText(), TEXT_PLACE_HOLDER)) {
                continue;
            }
            replaceParagraph(paragraph, dataMap);
        }
    }

    private static void replaceParagraph(XWPFParagraph paragraph, Map<String, Object> dataMap) throws Exception {
        List<XWPFRun> runList = paragraph.getRuns();

        if (CollectionUtils.isEmpty(runList)) {
            return;
        }
        int prev = -1;
        int prevIdx = 0;
        int last = -1;
        int lastIdx = 0;
        boolean prevExist = false;
        for (int i = 0; i < runList.size(); i++) {
            if (!CollectionUtils.isEmpty(runList.get(i).getEmbeddedPictures())) {
                continue;
            }
            if (StringUtils.isEmpty(runList.get(i).text())) {
                continue;
            }
            String runText = runList.get(i).text();
            if (runText.contains(PLACE_HOLDER_PREFIX) && !prevExist) {
                //处理同一个段落出现多个${}
                if (prev == i) {
                    prevIdx = runText.indexOf(PLACE_HOLDER_PREFIX, prevIdx + 1);
                } else {
                    prevIdx = runText.indexOf(PLACE_HOLDER_PREFIX);
                }
                prev = i;
                prevExist = true;
//                if (runText.indexOf(PLACE_HOLDER_PREFIX, prevIdx + 1) != -1) {
//                    i--;
//                }
            }
            if (runText.contains(PLACE_HOLDER_SUFFIX) && prevExist) {
                //处理同一个段落出现多个${}
                if (last == i) {
                    lastIdx = runText.indexOf(PLACE_HOLDER_SUFFIX, prevIdx + 1);
                } else {
                    lastIdx = runText.indexOf(PLACE_HOLDER_SUFFIX);
                }
                last = i;
                if (prev == -1 || last == -1) {
                    continue;
                }
                if (prevIdx == -1 || lastIdx == -1) {
                    continue;
                }

                String placeholderKey = concatRun(runList, prev, last, prevIdx, lastIdx);
                if (StringUtils.isEmpty(placeholderKey) || !PatternUtils.match(placeholderKey, TEXT_PLACE_HOLDER)) {
                    continue;
                }
                logger.info("placeholderKey:{}", placeholderKey);
                replaceTemplate(paragraph, runList, placeholderKey, prev, last, prevIdx, lastIdx, dataMap);
                prevExist = false;

                //判断当前run是否存在多个替换
                if (runText.indexOf(PLACE_HOLDER_PREFIX, lastIdx + 1) != -1) {
                    i--;
                }
            }
        }
    }

    private static void replaceTemplate(XWPFParagraph paragraph, List<XWPFRun> runList, String placeholderKey, int prev, int last, int prevIdx, int lastIdx, Map<String, Object> dataMap) throws Exception {
        String mapKey = PatternUtils.regex(placeholderKey, TEXT_PLACE_HOLDER, 1);
        Object value = obtainsValue(mapKey, dataMap);
        if (value == null) {
            value = "";
        }

        logger.info("mapKey:{}, mapValue:{}", mapKey, value);
        replaceRunTemplate(paragraph, runList, prev, last, prevIdx, lastIdx, value);
    }

    /**
     * 拼接多个run之间的内容,组成key
     *
     * @param runList
     * @param start
     * @param end
     * @param prevIdx
     * @param lastIdx
     * @return
     */
    private static String concatRun(List<XWPFRun> runList, int start, int end, int prevIdx, int lastIdx) {
        if (start == end) {
            // 形如}XXX${
            if (prevIdx > lastIdx) {
                return null;
            }
            if (runList.get(start).text().length() - 1 == lastIdx) {
                return runList.get(start).text().substring(prevIdx, lastIdx);
            }
            return runList.get(start).text().substring(prevIdx, lastIdx + 1);
        }
        StringBuffer stringBuffer = new StringBuffer();

        for (int i = start; i <= end; i++) {
            if (i == start) {
                stringBuffer.append(runList.get(i).text().substring(prevIdx));
            } else if (i == end) {
                stringBuffer.append(runList.get(i).text().substring(0, lastIdx + 1));
            } else {
                stringBuffer.append(runList.get(i).text());
            }
        }
        return stringBuffer.toString();
    }

    /**
     * 删除${}之间多余的内容
     *
     * @param runList
     * @param prev
     * @param last
     * @param prevIdx
     * @param lastIdx
     */
    private static void replaceRunTemplate(XWPFParagraph paragraph, List<XWPFRun> runList, int prev, int last, int prevIdx, int lastIdx, Object value) throws Exception {
        String prevRunText = runList.get(prev).text();
        String lastRunText = runList.get(last).text();

        logger.info("prev:{}-prevIdx:{}, last:{}-lastIdx:{}", prev, prevIdx, last, lastIdx);
        if (prev == last) {
            // 如果${}位于同一行，则删除key
            if (lastIdx == prevRunText.length() - 1) {
                runList.get(prev).setText(prevRunText.substring(0, prevIdx), 0);
                if (value instanceof ImageData) {
                    insertImgData(runList.get(prev), value);
                } else if (value instanceof ImageByteData) {
                    insertImgByteData(runList.get(prev), prev, value);
                } else {
                    runList.get(prev).setText(value.toString());
                }
            } else {
                String textBefore = prevRunText.substring(0, prevIdx);
                String textAfter = prevRunText.substring(lastIdx + 1);

                runList.get(prev).setText(textBefore, 0);
                if (value instanceof ImageData) {
                    insertImgData(runList.get(prev), value);
                } else if (value instanceof ImageByteData) {
                    insertImgByteData(runList.get(prev), prev, value);
                } else {
                    runList.get(prev).setText(value.toString());
                }
                runList.get(prev + 2).setText(textAfter);
            }
        } else {
            // 如果${}不位于同一行，则删除多余的run
            runList.get(prev).setText(prevRunText.substring(0, prevIdx), 0);
            if (value instanceof ImageData) {
                insertImgData(runList.get(prev), value);
            } else if (value instanceof ImageByteData) {
                insertImgByteData(runList.get(prev), prev, value);
            } else {
                runList.get(prev).setText(value.toString());
            }

            if (lastIdx == lastRunText.length() - 1) {
                runList.get(last).setText("", 0);
            } else {
                lastRunText = lastRunText.substring(lastIdx + 1);
                runList.get(last).setText(lastRunText, 0);
            }

            for (int i = prev + 1; i < last; i++) {
                runList.get(i).setText("", 0);
            }
        }
    }

    private static void insertImgByteData(XWPFRun run, int prev, Object value) throws Exception {
        ImageByteData imageByteData = (ImageByteData) value;

        byte[] bus = null;
        String ext = "jpeg";
        Matcher matcher = PatternUtils.BASE64_IMG_PATTERN.matcher(imageByteData.getBase64());
        if (matcher.find()) {
            ext = matcher.group(1);
            String rep = matcher.group(0);

            String imgBase64 = imageByteData.getBase64().replaceAll(rep, "");
            BASE64Decoder decoder = new BASE64Decoder();
            bus = decoder.decodeBuffer(imgBase64);
        }

        run.addPicture(new ByteArrayInputStream(bus), format(ext).nativeId, imageByteData.getFilename(), Units.toEMU(imageByteData.getWidth()), Units.toEMU(imageByteData.getHeight()));
    }

    private static void insertImgData(XWPFRun run, Object value) throws Exception {
        ImageData imageData = (ImageData) value;
        run.addPicture(new FileInputStream(imageData.getUrl()), format(imageData.getExt()).nativeId, imageData.getFilename(), Units.toEMU(imageData.getWidth()), Units.toEMU(imageData.getHeight()));
    }

    /**
     * 获取dataMap中key所对应的值
     *
     * @param mapKey
     * @param dataMap
     * @return
     */
    private static Object obtainsValue(String mapKey, Map<String, Object> dataMap) {
        if (!mapKey.contains(MULTI_FIELD_SEPERATOR)) {
            return dataMap.get(mapKey);
        }

        String[] fields = mapKey.split(MULTI_FIELD_SEPERATOR_SPLIT);
        Object tmpValue = dataMap;
        if (tmpValue == null) {
            return null;
        }
        for (int i = 0; i < fields.length; i++) {
            String field = fields[i];
            if (PatternUtils.match(field, INDEX_SEPERATOR_SPLIT)) {
                String key = PatternUtils.regex(field, INDEX_SEPERATOR_SPLIT_PATTERN, 1);
                String idx = PatternUtils.regex(field, INDEX_SEPERATOR_SPLIT_PATTERN, 2);
                if (tmpValue instanceof Map) {
                    Map map = (Map) tmpValue;
                    tmpValue = map.get(key);
                } else {
                    tmpValue = ClassUtils.getFieldValue(tmpValue, key);
                }
                tmpValue = ((List) tmpValue).get(Integer.parseInt(idx));
            } else {
                if (tmpValue instanceof Map) {
                    Map map = (Map) tmpValue;
                    tmpValue = map.get(field);
                } else {
                    tmpValue = ClassUtils.getFieldValue(tmpValue, field);
                }
            }
        }
        return tmpValue;
    }

    private static PictureData.PictureType format(String ext) {
        if ("JPEG".equalsIgnoreCase(ext)) {
            return PictureData.PictureType.JPEG;
        } else if ("PNG".equalsIgnoreCase(ext)) {
            return PictureData.PictureType.PNG;
        } else if ("SVG".equalsIgnoreCase(ext)) {
            return PictureData.PictureType.SVG;
        } else if ("GIF".equalsIgnoreCase(ext)) {
            return PictureData.PictureType.GIF;
        }
        return PictureData.PictureType.JPEG;
    }

    public static void main(String[] args) throws Exception {
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("name", "西门大桥");
        dataMap.put("age", "0000");
        dataMap.put("detectionTime", "2023-10-10");

        List<Object> objectList = new ArrayList<>();
        dataMap.put("results", objectList);
        dataMap.put("totalScore", "99.9");
        dataMap.put("totalLevel", "一类");

        ImageData imageData = new ImageData();
        imageData.setUrl("C:\\Users\\10419\\Desktop\\aaa.jpg");
        imageData.setExt("jpg");
        imageData.setFilename("aaa.jpg");
        dataMap.put("image", imageData);

        OPCPackage pack = POIXMLDocument.openPackage("C:\\Users\\10419\\Desktop\\test.docx");
        XWPFDocument document = new XWPFDocument(pack);//创建word对象

        replaceParagraphs(document, dataMap);

        FileOutputStream fos = new FileOutputStream("C:\\Users\\10419\\Desktop\\test1.docx");
        document.write(fos);
        fos.close();
    }

    private static void test() {
        Map<String, Object> dataMap = new HashMap<>();
        InnerTwo two = new InnerTwo();
        two.setRemark("remark");
        InnerTwo two2 = new InnerTwo();
        two2.setRemark("remark2");

        Map map = new HashMap();
        map.put("twos", Arrays.asList(two, two2));
        Inner inner = new Inner();
        inner.setName("fff");
        inner.setAge("gggg");
        inner.setKey(map);
//        inner.setTwos(Arrays.asList(two, two2));

        System.out.println(obtainsValue("tests[0].inner.key.twos[1].remark", dataMap));
        System.out.println(PatternUtils.match("tests[1].word", INDEX_SEPERATOR_SPLIT));
    }
}
